package com.jml.service;

import com.jml.constant.JmlConstant;
import com.jml.entity.GateWayEntity;
import com.jml.mapper.JmlGatewayMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
@Slf4j
public class JmlGatewayService implements ApplicationEventPublisherAware {
    @Autowired
    private JmlGatewayMapper jmlGatewayMapper;
    /**
     * 这个接口就是专门对api和路由进行增删改查
     */
    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;
    private ApplicationEventPublisher publisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher = applicationEventPublisher;
    }

    public void init() {
        // 1.获取到所有的 网关配置
        List<GateWayEntity> gateWayEntities = jmlGatewayMapper.gateWayAll();
        gateWayEntities.forEach(gateWayEntity -> {
            addConfig(gateWayEntity);
        });
    }

    /**
     * 新增配置
     * <p>
     * 思路：网关如何是集群 如何刷新路由策略呢？
     * 因为我们的网关都是注册nacos上 服务的名称都是相同的。
     * 编写代码 根据服务名称从nacos上获取网关的接口地址
     * 循环动态刷新 每个网关接口即可。
     */
    public void addConfig(GateWayEntity gateWayEntity) {
        RouteDefinition definition = new RouteDefinition();
        Map<String, String> predicateParams = new HashMap<>(8);
        PredicateDefinition predicate = new PredicateDefinition();
        FilterDefinition filterDefinition = new FilterDefinition();
        Map<String, String> filterParams = new HashMap<>(8);
        URI uri = null;
        if (JmlConstant.ROUTE_TYPE_LB.equals(gateWayEntity.getRouteType())) {
            uri = UriComponentsBuilder.fromUriString(gateWayEntity.getRouteUrl()).build().toUri();
        } else {
            uri = UriComponentsBuilder.fromHttpUrl(gateWayEntity.getRouteUrl()).build().toUri();
        }

        // 定义的路由唯一的id
        definition.setId(gateWayEntity.getRouteId());
        predicate.setName("Path");
        //路由转发地址
        predicateParams.put("pattern", gateWayEntity.getRoutePattern());
        predicate.setArgs(predicateParams);

        // 名称是固定的, 路径去前缀
        filterDefinition.setName("StripPrefix");
        filterParams.put("_genkey_0", "1");
        filterDefinition.setArgs(filterParams);
        definition.setPredicates(Arrays.asList(predicate));
        definition.setFilters(Arrays.asList(filterDefinition));
        definition.setUri(uri);
        routeDefinitionWriter.save(Mono.just(definition)).subscribe();
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        log.info("新增成功：" + gateWayEntity.toString());
    }

    @Autowired
    private ApplicationContext applicationContext;

    /**
     * 根据路由id删除网关相关配置
     *
     * @param routeId
     */
    public void deleteConfig(String routeId) {
        this.routeDefinitionWriter.delete(Mono.just(routeId))
                .then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
                .onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build())).subscribe();
    }
}
